package net.test.dao.impl;

import java.io.Serializable;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.Resource;


import net.test.bean.Pager;
import net.test.dao.BaseDao;
import net.test.entity.BaseEntity;
import net.test.util.ReflectionUtil;
import net.test.util.StringUtil;

import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.CriteriaSpecification;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projection;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.impl.CriteriaImpl;
import org.hibernate.impl.CriteriaImpl.OrderEntry;
import org.hibernate.metadata.ClassMetadata;
import org.hibernate.transform.ResultTransformer;
import org.springframework.beans.BeanUtils;
import org.springframework.util.Assert;


/**
 * Dao实现类 - 基类
 */

public class BaseDaoImpl<T, PK extends Serializable> implements BaseDao<T, PK> {

	private static final String ORDER_LIST_PROPERTY_NAME = "orderList";// "排序"属性名称

	private Class<T> entityClass;
	protected SessionFactory sessionFactory;
	private String web_site_id;

	@SuppressWarnings("unchecked")
	public BaseDaoImpl() {
		Class c = getClass();
		Type type = c.getGenericSuperclass();
		if (type instanceof ParameterizedType) {
			Type[] parameterizedType = ((ParameterizedType) type)
					.getActualTypeArguments();
			this.entityClass = (Class<T>) parameterizedType[0];
		}
	}

	@Resource(name = "sessionFactory")
	public void setSessionFactory(SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}

	protected Session getSession() {
		return sessionFactory.getCurrentSession();
	}

	@SuppressWarnings("unchecked")
	public T get(PK id) {
		Assert.notNull(id, "id is required");
		return (T) getSession().get(entityClass, id);
	}

	@SuppressWarnings("unchecked")
	public T load(PK id) {
		Assert.notNull(id, "id is required");
		return (T) getSession().load(entityClass, id);
	}

	@SuppressWarnings("unchecked")
	public List<T> getAllList() {
		ClassMetadata classMetadata = sessionFactory
				.getClassMetadata(entityClass);
		String _hql = "";
		if (ArrayUtils.contains(classMetadata.getPropertyNames(),
				ORDER_LIST_PROPERTY_NAME)) {
			_hql = "from " + entityClass.getName()
					+ " as entity order by entity." + ORDER_LIST_PROPERTY_NAME
					+ " desc";
		} else {
			_hql = "from " + entityClass.getName();
		}
		return getSession().createQuery(_hql).list();
	}

	public Long getTotalCount() {
		String hql = "select count(*) from " + entityClass.getName();
		return (Long) getSession().createQuery(hql).uniqueResult();
	}

	@SuppressWarnings("unchecked")
	public PK save(T entity) {
		Assert.notNull(entity, "entity is required");
		if (entity instanceof BaseEntity) {
			try {
				Method method = entity.getClass().getMethod(
						BaseEntity.ON_SAVE_METHOD_NAME);
				method.invoke(entity);
				return (PK) getSession().save(entity);
			} catch (Exception e) {
				e.printStackTrace();
				return null;
			}
		} else {
			return (PK) getSession().save(entity);
		}
	}

	public void update(T entity) {
		Assert.notNull(entity, "entity is required");
		if (entity instanceof BaseEntity) {
			try {
				Method method = entity.getClass().getMethod(
						BaseEntity.ON_UPDATE_METHOD_NAME);
				method.invoke(entity);
				getSession().update(entity);
			} catch (Exception e) {
				e.printStackTrace();
			}
		} else {
			getSession().update(entity);
		}
	}

	public void delete(T entity) {
		Assert.notNull(entity, "entity is required");
		getSession().delete(entity);
	}

	@SuppressWarnings("unchecked")
	public void delete(PK id) {
		Assert.notNull(id, "id is required");
		T entity = (T) getSession().load(entityClass, id);
		getSession().delete(entity);
	}

	@SuppressWarnings("unchecked")
	public void delete(PK[] ids) {
		Assert.notEmpty(ids, "ids must not be empty");
		for (PK id : ids) {
			T entity = (T) getSession().load(entityClass, id);
			getSession().delete(entity);
		}
	}

	public void flush() {
		getSession().flush();
	}

	public void evict(Object object) {
		Assert.notNull(object, "object is required");
		getSession().evict(object);
	}

	public void clear() {
		getSession().clear();
	}

	public Pager findPager(Pager pager) {
		Criteria criteria = getSession().createCriteria(entityClass);
		return findPager(pager, criteria);
	}

	public Pager findPager(Pager pager, boolean isMoreSearch) {
		Criteria criteria = getSession().createCriteria(entityClass);
		return findPager(pager, criteria, isMoreSearch);
	}

	public Pager findPager(Pager pager, Criterion... criterions) {
		Criteria criteria = getSession().createCriteria(entityClass);
		for (Criterion criterion : criterions) {
			criteria.add(criterion);
		}
		return findPager(pager, criteria);
	}

	public Pager findPager(Pager pager, Order... orders) {
		Criteria criteria = getSession().createCriteria(entityClass);
		for (Order order : orders) {
			criteria.addOrder(order);
		}
		return findPager(pager, criteria);
	}

	public Pager findPager(Pager pager, Criteria criteria) {
		Assert.notNull(pager, "pager is required");
		Assert.notNull(criteria, "criteria is required");

		Integer pageNumber = pager.getPageNumber();
		Integer pageSize = pager.getPageSize();
		String searchBy = pager.getSearchBy();
		String keyword = pager.getKeyword();
		String orderBy = pager.getOrderBy();
		Pager.Order order = pager.getOrder();

		if (StringUtils.isNotEmpty(searchBy) && StringUtils.isNotEmpty(keyword)) {
			if (searchBy.contains(".")) {
				String alias = StringUtils.substringBefore(searchBy, ".");
				criteria.createAlias(alias, alias);
			}
			criteria.add(Restrictions.like(searchBy, "%" + keyword + "%"));
		}

		pager.setTotalCount(criteriaResultTotalCount(criteria));

		if (!StringUtils.isNotEmpty(orderBy))
			orderBy = "createDate";
		if (order == null)
			order = Pager.Order.desc;
		if (StringUtils.isNotEmpty(orderBy) && order != null) {
			if (order == Pager.Order.asc) {
				criteria.addOrder(Order.asc(orderBy));
			} else {
				criteria.addOrder(Order.desc(orderBy));
			}
		}

		ClassMetadata classMetadata = sessionFactory
				.getClassMetadata(entityClass);
		if (!StringUtils.equals(orderBy, ORDER_LIST_PROPERTY_NAME)
				&& ArrayUtils.contains(classMetadata.getPropertyNames(),
						ORDER_LIST_PROPERTY_NAME)) {
			criteria.addOrder(Order.asc(ORDER_LIST_PROPERTY_NAME));
			if (StringUtils.isEmpty(orderBy) || order == null) {
				pager.setOrderBy(ORDER_LIST_PROPERTY_NAME);
				pager.setOrder(Pager.Order.asc);
			}
		}
		criteria.setFirstResult((pageNumber - 1) * pageSize);
		criteria.setMaxResults(pageSize);
		pager.setResult(criteria.list());
		return pager;
	}

	public Pager findPager(Pager pager, Criteria criteria, boolean isMoreSearch) {
		Criteria criteria2 = getSession().createCriteria(entityClass);
		BeanUtils.copyProperties(criteria, criteria2);
		Pager pager2 = new Pager();
		BeanUtils.copyProperties(pager, pager2);
		try {
			Assert.notNull(pager, "pager is required");
			Assert.notNull(criteria, "criteria is required");

			Integer pageNumber = pager.getPageNumber();
			Integer pageSize = pager.getPageSize();
			String searchBy = pager.getSearchBy();
			String keyword = pager.getKeyword();
			String orderBy = pager.getOrderBy();
			Pager.Order order = pager.getOrder();
			if (StringUtils.isNotEmpty(searchBy)
					&& StringUtils.isNotEmpty(keyword)) {
				String[] newSearchBy = searchBy.split("￡");
				String[] newKeyWord = keyword.split("￡");
				for (int i = 0; i < newSearchBy.length; i++) {
					if (StringUtils.isNotEmpty(newSearchBy[i])
							&& StringUtils.isNotEmpty(newKeyWord[i])
							&& !newSearchBy[i].equals("null")
							&& !newKeyWord[i].equals("null")) {
						if (newSearchBy[i].contains(".")) {
							String alias = StringUtils.substringBefore(
									newSearchBy[i], ".");
							criteria.createAlias(alias, alias);
						}
						if (StringUtil.isInteger(newKeyWord[i]))
							criteria.add(Restrictions.eq(newSearchBy[i],
									Integer.parseInt(newKeyWord[i])));
						else if ("isNull".equals(newKeyWord[i]))
							criteria.add(Restrictions.isNotNull(newSearchBy[i]));
						else if ("false".equals(newKeyWord[i])
								|| "true".equals(newKeyWord[i]))
							criteria.add(Restrictions.eq(newSearchBy[i],
									Boolean.parseBoolean(newKeyWord[i])));
						else {
							
								criteria.add(Restrictions.like(newSearchBy[i],
										"%" + newKeyWord[i] + "%"));
						}
					}
				}
			}

			pager.setTotalCount(criteriaResultTotalCount(criteria));

			if (!StringUtils.isNotEmpty(orderBy))
				orderBy = "createDate";
			if (order == null)
				order = Pager.Order.desc;

			if (StringUtils.isNotEmpty(orderBy) && order != null) {
				if (order == Pager.Order.asc) {
					criteria.addOrder(Order.asc(orderBy));
				} else {
					criteria.addOrder(Order.desc(orderBy));
				}
			}

			ClassMetadata classMetadata = sessionFactory
					.getClassMetadata(entityClass);
			if (!StringUtils.equals(orderBy, ORDER_LIST_PROPERTY_NAME)
					&& ArrayUtils.contains(classMetadata.getPropertyNames(),
							ORDER_LIST_PROPERTY_NAME)) {
				criteria.addOrder(Order.asc(ORDER_LIST_PROPERTY_NAME));
				if (StringUtils.isEmpty(orderBy) || order == null) {
					pager.setOrderBy(ORDER_LIST_PROPERTY_NAME);
					pager.setOrder(Pager.Order.asc);
				}
			}

			criteria.setFirstResult((pageNumber - 1) * pageSize);
			criteria.setMaxResults(pageSize);
			pager.setResult(criteria.list());
			return pager;
		} catch (ClassCastException e) {
			Assert.notNull(pager2, "pager is required");
			Assert.notNull(criteria2, "criteria is required");

			Integer pageNumber = pager2.getPageNumber();
			Integer pageSize = pager2.getPageSize();
			String searchBy = pager2.getSearchBy();
			String keyword = pager2.getKeyword();
			String orderBy = pager2.getOrderBy();
			Pager.Order order = pager2.getOrder();
			if (StringUtils.isNotEmpty(searchBy)
					&& StringUtils.isNotEmpty(keyword)) {
				String[] newSearchBy = searchBy.split("￡");
				String[] newKeyWord = keyword.split("￡");
				for (int i = 0; i < newSearchBy.length; i++) {
					if (StringUtils.isNotEmpty(newSearchBy[i])
							&& StringUtils.isNotEmpty(newKeyWord[i])
							&& !newSearchBy[i].equals("null")
							&& !newKeyWord[i].equals("null")) {
						if (newSearchBy[i].contains(".")) {
							String alias = StringUtils.substringBefore(
									newSearchBy[i], ".");
							criteria2.createAlias(alias, alias);
						}
						if ("isNull".equals(newKeyWord[i]))
							criteria2.add(Restrictions
									.isNotNull(newSearchBy[i]));
						else if ("false".equals(newKeyWord[i])
								|| "true".equals(newKeyWord[i]))
							criteria2.add(Restrictions.eq(newSearchBy[i],
									Boolean.parseBoolean(newKeyWord[i])));
						else {
							
								criteria.add(Restrictions.like(newSearchBy[i],
										"%" + newKeyWord[i] + "%"));
						}
					}
				}
			}

			pager2.setTotalCount(criteriaResultTotalCount(criteria2));

			if (!StringUtils.isNotEmpty(orderBy))
				orderBy = "createDate";
			if (order == null)
				order = Pager.Order.desc;

			if (StringUtils.isNotEmpty(orderBy) && order != null) {
				if (order == Pager.Order.asc) {
					criteria2.addOrder(Order.asc(orderBy));
				} else {
					criteria2.addOrder(Order.desc(orderBy));
				}
			}

			ClassMetadata classMetadata = sessionFactory
					.getClassMetadata(entityClass);
			if (!StringUtils.equals(orderBy, ORDER_LIST_PROPERTY_NAME)
					&& ArrayUtils.contains(classMetadata.getPropertyNames(),
							ORDER_LIST_PROPERTY_NAME)) {
				criteria2.addOrder(Order.asc(ORDER_LIST_PROPERTY_NAME));
				if (StringUtils.isEmpty(orderBy) || order == null) {
					pager2.setOrderBy(ORDER_LIST_PROPERTY_NAME);
					pager2.setOrder(Pager.Order.asc);
				}
			}

			criteria2.setFirstResult((pageNumber - 1) * pageSize);
			criteria2.setMaxResults(pageSize);

			pager2.setResult(criteria2.list());
			return pager2;
		}
	}

	// 获取Criteria查询数量
	@SuppressWarnings("unchecked")
	protected int criteriaResultTotalCount(Criteria criteria) {
		Assert.notNull(criteria, "criteria is required");

		int criteriaResultTotalCount = 0;
		try {
			CriteriaImpl criteriaImpl = (CriteriaImpl) criteria;

			Projection projection = criteriaImpl.getProjection();
			ResultTransformer resultTransformer = criteriaImpl
					.getResultTransformer();
			List<OrderEntry> orderEntries = (List) ReflectionUtil
					.getFieldValue(criteriaImpl, "orderEntries");
			ReflectionUtil.setFieldValue(criteriaImpl, "orderEntries",
					new ArrayList());

			Integer totalCount = ((Long) criteriaImpl.setProjection(
					Projections.rowCount()).uniqueResult()).intValue();
			if (totalCount != null) {
				criteriaResultTotalCount = totalCount;
			}

			criteriaImpl.setProjection(projection);
			if (projection == null) {
				criteriaImpl
						.setResultTransformer(CriteriaSpecification.ROOT_ENTITY);
			}
			if (resultTransformer != null) {
				criteriaImpl.setResultTransformer(resultTransformer);
			}
			ReflectionUtil.setFieldValue(criteriaImpl, "orderEntries",
					orderEntries);
		} catch (Exception e) {

		}
		return criteriaResultTotalCount;
	}

	// 得到当前登录的供应商下所属订单，以及按条件查询订单
	public List getListByProperty(Map<String, String> params,
			String objectName, String objectValue, Pager pager) {
		StringBuffer hqlBuffer = new StringBuffer("from" + " " + objectName
				+ " as " + objectValue);
		if (params != null) {
			Set<String> set = params.keySet();
			if (set != null) {
				hqlBuffer.append(" where ");
				boolean bool = true;
				for (String key : set) {
					if (bool) {
						if (params.get(key).equals("null")) {
							hqlBuffer.append(objectValue + "." + key
									+ " is null");
						} else if (params.get(key).equals("notNull")) {
							hqlBuffer.append(objectValue + "." + key
									+ " is not null");
						} else {
							hqlBuffer.append(objectValue + "." + key + "='"
									+ params.get(key) + "' ");
						}
					} else {
						if (params.get(key).equals("null")) {
							hqlBuffer.append("and " + objectValue + "." + key
									+ " is null");
						} else if (params.get(key).equals("notNull")) {
							hqlBuffer.append("and " + objectValue + "." + key
									+ " is not null");
						} else {
							hqlBuffer.append("and " + objectValue + "." + key
									+ "='" + params.get(key) + "' ");
						}
					}
					bool = false;
				}
			}
		}
		// 判断pager对象，堆加 order by 和排序条件
		if (pager != null) {
			if (pager.getOrderBy() != null
					&& !pager.getOrderBy().trim().equals("")) {
				hqlBuffer.append(" order by " + pager.getOrderBy());
			} else {
				hqlBuffer.append(" order by createDate");
			}
			if (pager.getOrder() != null) {
				hqlBuffer.append(" " + pager.getOrder());
			} else {
				hqlBuffer.append(" desc");
			}
		}
		String hql = hqlBuffer.toString();
		// System.out.println(hql);
		return getSession().createQuery(hql).list();
	}

	public Integer getCountByProperty(Map<String, String> params,
			String objectName, String objectValue) {
		Integer count = 0;
		StringBuffer hqlBuffer = new StringBuffer("from" + " " + objectName
				+ " as " + objectValue);
		if (params != null) {
			Set<String> set = params.keySet();

			if (set != null) {
				hqlBuffer.append(" where ");
				boolean bool = true;
				for (String key : set) {
					if (bool) {
						if (params.get(key) != null) {
							hqlBuffer.append(objectValue + "." + key + "='"
									+ params.get(key) + "' ");
						} else {
							hqlBuffer.append(objectValue + "." + key
									+ " is null");
						}
					} else {
						if (params.get(key).equals("null")) {
							hqlBuffer.append("and " + objectValue + "." + key
									+ " is null");
						} else if (params.get(key).equals("notNull")) {
							hqlBuffer.append("and " + objectValue + "." + key
									+ " is not null");
						} else {
							hqlBuffer.append("and " + objectValue + "." + key
									+ "='" + params.get(key) + "' ");
						}
					}
					bool = false;
				}
			}
		}
		String hql = hqlBuffer.toString();
		List list = getSession().createQuery(hql).list();
		if (list != null) {
			count = list.size();
		}
		return count;
	}

	// 獲取列名
	public String[] getColumnName() {
		ClassMetadata databaseMetaData = getSession().getSessionFactory()
				.getClassMetadata(entityClass);
		return databaseMetaData.getPropertyNames();
	}

	public Class<T> getEntityClass() {
		return entityClass;
	}

	public void setEntityClass(Class<T> entityClass) {
		this.entityClass = entityClass;
	}

	public List findObjByHql(String hql) {
		List list = getSession().createQuery(hql).list();
		if (list == null || list.size() == 0)
			return null;
		else
			return list;
	}

	public List findObjByHql(String hql, int page, int size) {
		Query query = (Query) getSession().createCriteria(hql);
		query.setFirstResult((page * size - size));
		query.setMaxResults(size);
		List list = query.list();
		if (list == null || list.size() == 0)
			return null;
		else
			return list;
	}

}